{
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7-final"
  },
  "orig_nbformat": 2,
  "kernelspec": {
   "name": "python37764bitdlenvconda1763085c40a848e3ae8a8d3a98a401cd",
   "display_name": "Python 3.7.7 64-bit ('dl_env': conda)"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2,
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pytorch 基本操作\n",
    "本文记录 pytorch 的所有基本操作\n",
    "\n",
    "## 1. 如何创建 Tensor 数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入 torch\n",
    "import torch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "<class 'torch.Tensor'>\ntensor([[1.1210e-44, 0.0000e+00, 0.0000e+00],\n        [0.0000e+00, 0.0000e+00, 0.0000e+00],\n        [0.0000e+00, 0.0000e+00, 0.0000e+00],\n        [0.0000e+00, 0.0000e+00, 0.0000e+00],\n        [0.0000e+00, 0.0000e+00, 0.0000e+00]])\n"
    }
   ],
   "source": [
    "# 生成一个 5 行 3 列的空数组 tensor.Tensor\n",
    "x = torch.empty(5, 3)\n",
    "print(type(x))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "tensor([[0.1245, 0.6975, 0.7977, 0.5149, 0.3187, 0.3112, 0.1004, 0.5417],\n        [0.0871, 0.9628, 0.8674, 0.9503, 0.5918, 0.3047, 0.3252, 0.3105],\n        [0.1050, 0.9359, 0.6772, 0.4963, 0.7597, 0.9632, 0.4298, 0.8187],\n        [0.1360, 0.7499, 0.8232, 0.3399, 0.0793, 0.9399, 0.5984, 0.3133],\n        [0.1507, 0.3674, 0.2935, 0.0096, 0.5071, 0.4639, 0.4668, 0.3080],\n        [0.3856, 0.3646, 0.7828, 0.0869, 0.5972, 0.0593, 0.2914, 0.2122],\n        [0.7267, 0.6387, 0.4375, 0.9079, 0.4229, 0.0198, 0.9414, 0.3454],\n        [0.1516, 0.5817, 0.9999, 0.7018, 0.6700, 0.0359, 0.0171, 0.6607],\n        [0.6269, 0.2930, 0.8997, 0.2511, 0.6004, 0.4931, 0.0458, 0.5022],\n        [0.2870, 0.8298, 0.7846, 0.4154, 0.0224, 0.1657, 0.9532, 0.4840]])\n"
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "tensor([[7.3736, 9.0925, 9.3932, 8.5447, 7.9560, 7.9337, 7.3011, 8.6252],\n        [7.2613, 9.8885, 9.6023, 9.8509, 8.7754, 7.9141, 7.9757, 7.9315],\n        [7.3149, 9.8077, 9.0315, 8.4889, 9.2792, 9.8896, 8.2893, 9.4562],\n        [7.4080, 9.2497, 9.4695, 8.0197, 7.2378, 9.8197, 8.7952, 7.9398],\n        [7.4520, 8.1023, 7.8806, 7.0289, 8.5213, 8.3918, 8.4004, 7.9240],\n        [8.1567, 8.0937, 9.3485, 7.2608, 8.7917, 7.1778, 7.8743, 7.6365],\n        [9.1800, 8.9162, 8.3124, 9.7238, 8.2686, 7.0594, 9.8243, 8.0363],\n        [7.4548, 8.7450, 9.9997, 9.1054, 9.0100, 7.1077, 7.0514, 8.9821],\n        [8.8807, 7.8791, 9.6990, 7.7532, 8.8012, 8.4794, 7.1373, 8.5066],\n        [7.8609, 9.4894, 9.3539, 8.2461, 7.0673, 7.4972, 9.8596, 8.4520]])"
     },
     "metadata": {},
     "execution_count": 8
    }
   ],
   "source": [
    "# 生成一个 10 行 8 列的 随机数组，所有的值都在 0-1 直接\n",
    "x = torch.rand(10, 8)\n",
    "print(x)\n",
    "# 随机数变成 7 - 10 之间呢\n",
    "(10-7)*x + 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "torch.int64\ntorch.float32\n"
    }
   ],
   "source": [
    "# 创建一个5x3的long型全0的Tensor, 默认是 float32\n",
    "x = torch.zeros(5, 3, dtype=torch.int64)\n",
    "print(x.dtype)\n",
    "print(torch.zeros(4).dtype)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "tensor([[2., 3., 4.],\n        [4., 5., 6.]])"
     },
     "metadata": {},
     "execution_count": 15
    }
   ],
   "source": [
    "# 直接根据list创建\n",
    "torch.Tensor([[2, 3, 4], [4, 5, 6]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "tensor([[1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.]])\n"
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "tensor([[ 0.4574, -1.0036, -1.5763, -0.2931, -0.0361],\n        [ 0.6433,  0.8529,  0.8333, -0.1339,  1.0698],\n        [-1.8877, -0.2517,  0.2349, -1.1219, -0.2981],\n        [-0.1068,  2.0253,  1.1612,  0.3231,  0.4163]])"
     },
     "metadata": {},
     "execution_count": 21
    }
   ],
   "source": [
    "# 还可以根据已经存在的 tensor 来创建新的 tensor，比较少用\n",
    "x = torch.ones(4, 5)\n",
    "print(x)\n",
    "x.new_ones(3, 4) # 相同 dtype\n",
    "torch.randn_like(x) # 服从0，1正态分布，指定新的数据类型，相同的 shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 如何查看 Tensor 数组元数据\n",
    "+ dtype 属性查看 Tensor 的数据类型\n",
    "+ size() 或者 shape 属性查看 Tensor 的形状"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "torch.float32\ntorch.Size([4, 5])\ntorch.Size([4, 5])\n"
    }
   ],
   "source": [
    "print(x.dtype)\n",
    "print(x.size())\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 如何对 Tensor 进行数值计算\n",
    "Tensor 是数组，所以数值计算指的是数组的向量计算\n",
    "加减乘除、点乘"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "tensor([[1.1034, 0.4275, 1.1121],\n        [0.9648, 1.0323, 0.5832]])\ntensor([[1.1034, 0.4275, 1.1121],\n        [0.9648, 1.0323, 0.5832]])\ntensor([[-0.6121,  0.0600,  0.5946],\n        [-0.8870, -0.2099,  0.1984]])\ntensor([[-0.6121,  0.0600,  0.5946],\n        [-0.8870, -0.2099,  0.1984]])\ntensor([[0.2107, 0.0448, 0.2208],\n        [0.0360, 0.2554, 0.0752]])\ntensor([[0.2107, 0.0448, 0.2208],\n        [0.0360, 0.2554, 0.0752]])\ntensor([[0.2864, 1.3268, 3.2975],\n        [0.0420, 0.6620, 2.0315]])\ntensor([[0.2864, 1.3268, 3.2975],\n        [0.0420, 0.6620, 2.0315]])\n"
    }
   ],
   "source": [
    "# 两个相同规模的 Tensor 相加、减、乘、除，就是对应位置相加、减、乘、除\n",
    "# 所有操作除了直接用运算符，也可以通过 torch 对应的方法来操作\n",
    "x = torch.rand(2, 3)\n",
    "y = torch.rand(2, 3)\n",
    "print(x+y)\n",
    "print(torch.add(x, y))\n",
    "print(x-y)\n",
    "print(torch.sub(x, y))\n",
    "print(x*y)\n",
    "print(torch.mul(x, y))\n",
    "print(x/y)\n",
    "print(torch.div(x, y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "tensor([[0.2457, 0.2437, 0.8533],\n        [0.0389, 0.4112, 0.3908]])\ntensor([[0., 0., 0.],\n        [0., 0., 0.]])\ntensor([[0.2457, 0.2437, 0.8533],\n        [0.0389, 0.4112, 0.3908]])\n"
    }
   ],
   "source": [
    "# 还有重要的操作就是 原地操作\n",
    "# 上面的操作最终都会返回一个新的 Tensor，但是原地操作就是直接将旧的Tensor里面的值更新为新数值，更加节省内存\n",
    "# PyTorch操作inplace版本都有后缀_, 例如 x.copy_(y), x.t_()\n",
    "print(x)\n",
    "z = torch.zeros(2, 3)\n",
    "print(z)\n",
    "z.add_(x)\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Tensor 数组索引操作    \n",
    "索引操作是指在原来 Tensor 中取出部分元素的操作     \n",
    "PyTorch 的索引操作均是与原来 Tensor 共享内存，即改变索引操作得到的子数组的数值，原来数值的对应位置的值也会改变"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "tensor([[1., 0., 0., 0.],\n        [0., 2., 0., 0.],\n        [0., 0., 3., 0.],\n        [0., 0., 0., 4.]])\ntensor([[1.],\n        [0.],\n        [0.],\n        [0.]])\ntensor([[1., 0., 0., 0.],\n        [0., 2., 0., 0.],\n        [2., 0., 3., 0.],\n        [0., 0., 0., 4.]])\n"
    }
   ],
   "source": [
    "# 取出某一列，和 numpy 的操作基本类似\n",
    "x = torch.diag(torch.Tensor([1, 2, 3, 4]))\n",
    "print(x)\n",
    "y = x[:, 0:1]\n",
    "print(y)\n",
    "y[2] = 2\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 改变 Tensor 的形状\n",
    "一般用 view 来操作，返回的操作也是和原来 Tensor 共享内存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "tensor([[1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.]])\ntensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n        1., 1.])\ntensor([[0., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.]])\n"
    }
   ],
   "source": [
    "x = torch.ones(4, 5)\n",
    "print(x)\n",
    "y = x.view(20)\n",
    "print(y)\n",
    "y[0] = 0\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": "tensor([[1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.]])\ntensor([[1., 1., 1., 1.],\n        [1., 1., 1., 1.],\n        [1., 1., 1., 1.],\n        [1., 1., 1., 1.],\n        [1., 1., 1., 1.]])\ntensor([[1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.],\n        [1., 1., 1., 1., 1.]])\n"
    }
   ],
   "source": [
    "# 如果想得到一个真正的副本，那就得使用 clone 函数\n",
    "x = torch.ones(4, 5)\n",
    "print(x)\n",
    "y = x.clone().view(5, 4)\n",
    "print(y)\n",
    "y[0, 0] = 0\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另外一个常用的函数就是item(), 它可以将一个标量Tensor转换成一个Python number"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Tensor 线性代数操作\n",
    "下面的函数都是 torch 的函数，所有的矩阵基本线性代数操作都可以完成  \n",
    "   \n",
    "|  函数 |  功能 | \n",
    "|  ----  | ----  |\n",
    "|  trace |  对角线元素之和(矩阵的迹) | \n",
    "|  diag |  对角线元素 | \n",
    "|  triu/tril |  矩阵的上三角/下三角，可指定偏移量 | \n",
    "|  mm/bmm |  矩阵乘法，batch的矩阵乘法 | \n",
    "|  addmm/addbmm/addmv/addr/baddbmm.. |  矩阵运算 | \n",
    "|  t |  转置 | \n",
    "|  dot/cross |  内积/外积 | \n",
    "|  inverse |  求逆矩阵 | \n",
    "|  svd |  奇异值分解 | "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后需要注意 Tensor 的广播机制、内存开销、以及和 numpy 相互转换\n",
    "Tensor 如果通过 numpy from_numpy 操作得到新的 Tensor，它们之间是相互共享内存的\n",
    "广播机制和 numpy 差不多"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ]
}